/*
 * px.c
 *		PostgreSQL 的各种加密功能。
 *
 * Copyright (c) 2001 Marko Kreen
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *	  notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *	  notice, this list of conditions and the following disclaimer in the
 *	  documentation和/或其他材料提供的发行版本中。
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, 数据，或者利润; 或业务中断)
 * HOWEVER CAUSED AND ON ANY理论的责任，不论是合同、严格责任还是侵权（包括疏忽或其他）以任何方式引起的
 * 使用本软件，即使在被告知可能性时也不承担任何责任。
 *
 * contrib/pgcrypto/px.c
 */

#include "postgres.h"

#include "px.h"

struct error_desc
{
	int			err;
	const char *desc;
};

static const struct error_desc px_err_list[] = {
	{PXE_OK, "Everything ok"},
	{PXE_NO_HASH, "No such hash algorithm"},
	{PXE_NO_CIPHER, "No such cipher algorithm"},
	{PXE_BAD_OPTION, "Unknown option"},
	{PXE_BAD_FORMAT, "Badly formatted type"},
	{PXE_KEY_TOO_BIG, "Key was too big"},
	{PXE_CIPHER_INIT, "Cipher cannot be initialized"},
	{PXE_HASH_UNUSABLE_FOR_HMAC, "This hash algorithm is unusable for HMAC"},
	{PXE_BUG, "pgcrypto bug"},
	{PXE_ARGUMENT_ERROR, "Illegal argument to function"},
	{PXE_UNKNOWN_SALT_ALGO, "Unknown salt algorithm"},
	{PXE_BAD_SALT_ROUNDS, "Incorrect number of rounds"},
	{PXE_NO_RANDOM, "Failed to generate strong random bits"},
	{PXE_DECRYPT_FAILED, "Decryption failed"},
	{PXE_ENCRYPT_FAILED, "Encryption failed"},
	{PXE_PGP_CORRUPT_DATA, "Wrong key or corrupt data"},
	{PXE_PGP_CORRUPT_ARMOR, "Corrupt ascii-armor"},
	{PXE_PGP_UNSUPPORTED_COMPR, "Unsupported compression algorithm"},
	{PXE_PGP_UNSUPPORTED_CIPHER, "Unsupported cipher algorithm"},
	{PXE_PGP_UNSUPPORTED_HASH, "Unsupported digest algorithm"},
	{PXE_PGP_COMPRESSION_ERROR, "Compression error"},
	{PXE_PGP_NOT_TEXT, "Not text data"},
	{PXE_PGP_UNEXPECTED_PKT, "Unexpected packet in key data"},
	{PXE_PGP_MATH_FAILED, "Math operation failed"},
	{PXE_PGP_SHORT_ELGAMAL_KEY, "Elgamal keys must be at least 1024 bits long"},
	{PXE_PGP_UNKNOWN_PUBALGO, "Unknown public-key encryption algorithm"},
	{PXE_PGP_WRONG_KEY, "Wrong key"},
	{PXE_PGP_MULTIPLE_KEYS,
	"Several keys given - pgcrypto does not handle keyring"},
	{PXE_PGP_EXPECT_PUBLIC_KEY, "Refusing to encrypt with secret key"},
	{PXE_PGP_EXPECT_SECRET_KEY, "Cannot decrypt with public key"},
	{PXE_PGP_NOT_V4_KEYPKT, "Only V4 key packets are supported"},
	{PXE_PGP_KEYPKT_CORRUPT, "Corrupt key packet"},
	{PXE_PGP_NO_USABLE_KEY, "No encryption key found"},
	{PXE_PGP_NEED_SECRET_PSW, "Need password for secret key"},
	{PXE_PGP_BAD_S2K_MODE, "Bad S2K mode"},
	{PXE_PGP_UNSUPPORTED_PUBALGO, "Unsupported public key algorithm"},
	{PXE_PGP_MULTIPLE_SUBKEYS, "Several subkeys not supported"},

	{0, NULL},
};

/*
 * 调用 ereport(ERROR, ...)，使用与作为参数给出的 PXE_* 错误代码相对应的错误代码和消息。
 *
 * 这类似于 px_strerror(err)，但对于某些错误，我们更恰当地填写错误代码和详细信息字段。
 */
void px_THROW_ERROR(int fc_err)
{
	if (fc_err == PXE_NO_RANDOM)
	{
		ereport(ERROR,
				(errcode(ERRCODE_INTERNAL_ERROR),
				 errmsg("could not generate a random number")));
	}
	else
	{
		/* 对于其他错误，请使用上述列表中的消息。 */
		ereport(ERROR,
				(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
				 errmsg("%s", px_strerror(fc_err))));
	}
}

const char * px_strerror(int fc_err)
{
	const struct error_desc *fc_e;

	for (fc_e = px_err_list; fc_e->desc; fc_e++)
		if (fc_e->err == fc_err)
			return fc_e->desc;
	return "Bad error code";
}

/* 必须保留的 memset */
void px_memset(void *fc_ptr, int fc_c, size_t fc_len)
{
	memset(fc_ptr, fc_c, fc_len);
}

const char * px_resolve_alias(const PX_Alias *fc_list, const char *fc_name)
{
	while (fc_list->name)
	{
		if (pg_strcasecmp(fc_list->alias, fc_name) == 0)
			return fc_list->name;
		fc_list++;
	}
	return fc_name;
}

static void (*debug_handler) (const char *) = NULL;

void px_set_debug_handler(void (*fc_handler) (const char *))
{
	debug_handler = fc_handler;
}

void px_debug(const char *fc_fmt,...)
{
	va_list		fc_ap;

	va_start(fc_ap, fc_fmt);
	if (debug_handler)
	{
		char		fc_buf[512];

		vsnprintf(fc_buf, sizeof(fc_buf), fc_fmt, fc_ap);
		debug_handler(fc_buf);
	}
	va_end(fc_ap);
}

/*
 * combo - 密码 + 填充 (+ 校验和)
 */

static unsigned fc_combo_encrypt_len(PX_Combo *fc_cx, unsigned fc_dlen)
{
	return fc_dlen + 512;
}

static unsigned fc_combo_decrypt_len(PX_Combo *fc_cx, unsigned fc_dlen)
{
	return fc_dlen;
}

static int fc_combo_init(PX_Combo *fc_cx, const uint8 *fc_key, unsigned fc_klen,
		   const uint8 *fc_iv, unsigned fc_ivlen)
{
	int			fc_err;
	unsigned	fc_ks,
				fc_ivs;
	PX_Cipher  *fc_c = fc_cx->cipher;
	uint8	   *fc_ivbuf = NULL;
	uint8	   *fc_keybuf;

	fc_ks = px_cipher_key_size(fc_c);

	fc_ivs = px_cipher_iv_size(fc_c);
	if (fc_ivs > 0)
	{
		fc_ivbuf = palloc0(fc_ivs);
		if (fc_ivlen > fc_ivs)
			memcpy(fc_ivbuf, fc_iv, fc_ivs);
		else if (fc_ivlen > 0)
			memcpy(fc_ivbuf, fc_iv, fc_ivlen);
	}

	if (fc_klen > fc_ks)
		fc_klen = fc_ks;
	fc_keybuf = palloc0(fc_ks);
	memset(fc_keybuf, 0, fc_ks);
	memcpy(fc_keybuf, fc_key, fc_klen);

	fc_err = px_cipher_init(fc_c, fc_keybuf, fc_klen, fc_ivbuf);

	if (fc_ivbuf)
		pfree(fc_ivbuf);
	pfree(fc_keybuf);

	return fc_err;
}

static int fc_combo_encrypt(PX_Combo *fc_cx, const uint8 *fc_data, unsigned fc_dlen,
			  uint8 *fc_res, unsigned *fc_rlen)
{
	return px_cipher_encrypt(fc_cx->cipher, fc_cx->padding, fc_data, fc_dlen, fc_res, fc_rlen);
}

static int fc_combo_decrypt(PX_Combo *fc_cx, const uint8 *fc_data, unsigned fc_dlen,
			  uint8 *fc_res, unsigned *fc_rlen)
{
	return px_cipher_decrypt(fc_cx->cipher, fc_cx->padding, fc_data, fc_dlen, fc_res, fc_rlen);
}

static void fc_combo_free(PX_Combo *fc_cx)
{
	if (fc_cx->cipher)
		px_cipher_free(fc_cx->cipher);
	px_memset(fc_cx, 0, sizeof(*fc_cx));
	pfree(fc_cx);
}

/* 解析器 */

static int fc_parse_cipher_name(char *fc_full, char **fc_cipher, char **fc_pad)
{
	char	   *fc_p,
			   *fc_p2,
			   *fc_q;

	*fc_cipher = fc_full;
	*fc_pad = NULL;

	fc_p = strchr(fc_full, '/');
	if (fc_p != NULL)
		*fc_p++ = 0;
	while (fc_p != NULL)
	{
		if ((fc_q = strchr(fc_p, '/')) != NULL)
			*fc_q++ = 0;

		if (!*fc_p)
		{
			fc_p = fc_q;
			continue;
		}
		fc_p2 = strchr(fc_p, ':');
		if (fc_p2 != NULL)
		{
			*fc_p2++ = 0;
			if (strcmp(fc_p, "pad") == 0)
				*fc_pad = fc_p2;
			else
				return PXE_BAD_OPTION;
		}
		else
			return PXE_BAD_FORMAT;

		fc_p = fc_q;
	}
	return 0;
}

/* 提供者 */

int px_find_combo(const char *fc_name, PX_Combo **fc_res)
{
	int			fc_err;
	char	   *fc_buf,
			   *fc_s_cipher,
			   *fc_s_pad;

	PX_Combo   *fc_cx;

	fc_cx = palloc0(sizeof(*fc_cx));
	fc_buf = pstrdup(fc_name);

	fc_err = fc_parse_cipher_name(fc_buf, &fc_s_cipher, &fc_s_pad);
	if (fc_err)
	{
		pfree(fc_buf);
		pfree(fc_cx);
		return fc_err;
	}

	fc_err = px_find_cipher(fc_s_cipher, &fc_cx->cipher);
	if (fc_err)
		goto err1;

	if (fc_s_pad != NULL)
	{
		if (strcmp(fc_s_pad, "pkcs") == 0)
			fc_cx->padding = 1;
		else if (strcmp(fc_s_pad, "none") == 0)
			fc_cx->padding = 0;
		else
			goto err1;
	}
	else
		fc_cx->padding = 1;

	fc_cx->init = fc_combo_init;
	fc_cx->encrypt = fc_combo_encrypt;
	fc_cx->decrypt = fc_combo_decrypt;
	fc_cx->encrypt_len = fc_combo_encrypt_len;
	fc_cx->decrypt_len = fc_combo_decrypt_len;
	fc_cx->free = fc_combo_free;

	pfree(fc_buf);

	*fc_res = fc_cx;

	return 0;

err1:
	if (fc_cx->cipher)
		px_cipher_free(fc_cx->cipher);
	pfree(fc_cx);
	pfree(fc_buf);
	return PXE_NO_CIPHER;
}
